import { getCenter, getHeight, getWidth } from 'ol/extent'
import { fromLonLat as olFromLonLat, toLonLat as olToLonLat } from 'ol/proj'
import Projection from 'ol/proj/Projection'
import { Circle as CircleStyle, Fill, Stroke, Style, RegularShape } from 'ol/style'
import { transform } from 'ol/proj'
import { getDistance } from 'ol/sphere'
import * as turf from '@turf/turf'

/**
 * 判断数据类型函数
 * @param {*} value  any
 */
export function toRawType(value) {
	return Object.prototype.toString.call(value).slice(8, -1)
}

/** Object 类型 **/
export function isObject(val) {
	return toRawType(val) === 'Object'
}

/** 字符串 类型 **/
export function isString(val) {
	return typeof val === 'string' || val instanceof String
}

/** 函数 类型 **/
export function isFunction(val) {
	return typeof val === 'function'
}

/** WMapLngLat 类型 **/
export function isWMapLngLat(val) {
	return Object.hasOwnProperty.call(val, 'olat') && Object.hasOwnProperty.call(val, 'olng')
}

/**
 * 经纬度转投影坐标
 * @param {*} val  经纬度
 */
export function fromLonLat(val, projection = 'EPSG:3857') {
	if (isWMapLngLat(val)) {
		return [val.olng, val.olat]
	} else if (val) {
		return olFromLonLat(val, projection).map(n => toSureFixed(n, 6))
	} else {
		return null
	}
}

/**
 * 投影坐标转经纬度
 * @param {*} val  坐标
 */
export function toLonLat(val, projection = 'EPSG:3857') {
	if (isWMapLngLat(val)) {
		return [val.lng, val.lat]
	} else if (val) {
		return olToLonLat(val, projection).map(n => toSureFixed(n, 6))
	} else {
		return null
	}
}

/**
 * 16进制颜色转为RGB格式
 * @param {*} sHex  16进制颜色
 * @param {*} opacity 透明度
 */
export function toColorRgba(sHex, opacity = 1) {
	// 十六进制颜色值的正则表达式
	var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/
	/* 16进制颜色转为RGB格式 */
	var sColor = sHex.toLowerCase()
	var alpha = 1
	if (sColor && reg.test(sColor)) {
		if (sColor.length === 4 || sColor.length === 5) {
			var sColorNew = '#'
			for (var i = 1; i < sColor.length; i += 1) {
				sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
			}
			sColor = sColorNew
		}
		// 如果有透明度再执行
		if (sColor.length === 9) {
			alpha = (parseInt('0x' + sColor.slice(7, 9)) / 255).toFixed(2)
		}
		alpha = Number(opacity) !== 1 ? alpha : Number(opacity)
		//  处理六位的颜色值
		var sColorChange = []
		for (var i = 1; i < 7; i += 2) {
			sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)))
		}
		return 'rgba(' + sColorChange.join(',') + ',' + alpha + ')'
	} else {
		return sColor
	}
}

/*
 * 获取在线天地图
 * type:获取的瓦片类型，影像、矢量
 * projection:坐标系
 * token:官网申请的开发者token
 */
export function getLayerUrlByData(type, projection, token) {
	var url = '',
		layerId,
		tileMatrixSetId
	if (type === 'image') {
		url = 'http://t{1-7}.tianditu.com/DataServer?'
		layerId = 'img_'
		tileMatrixSetId = projection === 4326 ? 'c' : 'w'
	} else if (type === 'label') {
		url = 'http://t{1-7}.tianditu.com/DataServer?'
		layerId = 'cia_'
		tileMatrixSetId = projection === 4326 ? 'c' : 'w'
	} else if (type === 'street') {
		url = 'http://t{1-7}.tianditu.com/DataServer?'
		layerId = 'vec_'
		tileMatrixSetId = projection === 4326 ? 'c' : 'w'
	} else if (type === 'street_label') {
		url = 'http://t{1-7}.tianditu.com/DataServer?'
		layerId = 'cva_'
		tileMatrixSetId = projection === 4326 ? 'c' : 'w'
	}
	return url + 'T=' + layerId + tileMatrixSetId + '&x={x}&y={y}&l={z}&tk=' + token
}

/**
 * 获取地图在线瓦片图层相关参数
 * @param {*} mapName  地图类型
 * @param {*} mapType 取的瓦片类型，影像、矢量
 * @param {*} token  官网申请的开发者token
 * @param {*} projection  坐标系
 */
export function getMapBaseLayerOptions(mapType, projection) {
	// 高德地图底图链接
	const amapStandardTileLayerUrl = 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
	const amapSatelliteTileLayerUrl1 = 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=6&x={x}&y={y}&z={z}'
	const amapSatelliteTileLayerUrl2 = 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=8&x={x}&y={y}&z={z}'

	//注意： TileLayer的source的projection 与 View 的source的projection 可能不一样
	const mapBaseLayer = {
			standard: [[amapStandardTileLayerUrl, projection]],
			satellite: [
				[amapSatelliteTileLayerUrl1, projection],
				[amapSatelliteTileLayerUrl2, projection]
			]
	}

	return mapBaseLayer[mapType]
}

/**
 * 深度拷贝
 * @param {*} target  克隆对象
 * @param {*} map 解决循环引用
 */
export function deepClone(target, map = new WeakMap()) {
	const mapTag = '[object Map]'
	const setTag = '[object Set]'
	const arrayTag = '[object Array]'
	const objectTag = '[object Object]'
	const argsTag = '[object Arguments]'

	const boolTag = '[object Boolean]'
	const dateTag = '[object Date]'
	const numberTag = '[object Number]'
	const stringTag = '[object String]'
	const symbolTag = '[object Symbol]'
	const errorTag = '[object Error]'
	const regexpTag = '[object RegExp]'
	const funcTag = '[object Function]'

	const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]
	function forEach(array, iteratee) {
		let index = -1
		const length = array.length
		while (++index < length) {
			iteratee(array[index], index)
		}
		return array
	}

	function isObject(target) {
		const type = typeof target
		return target !== null && (type === 'object' || type === 'function')
	}

	function getType(target) {
		return Object.prototype.toString.call(target)
	}

	function getInit(target) {
		const Ctor = target.constructor
		return new Ctor()
	}

	function cloneSymbol(targe) {
		return Object(Symbol.prototype.valueOf.call(targe))
	}

	function cloneReg(targe) {
		const reFlags = /\w*$/
		const result = new targe.constructor(targe.source, reFlags.exec(targe))
		result.lastIndex = targe.lastIndex
		return result
	}

	function cloneFunction(func) {
		const bodyReg = /(?<={)(.|\n)+(?=})/m
		const paramReg = /(?<=\().+(?=\)\s+{)/
		const funcString = func.toString()
		if (func.prototype) {
			const param = paramReg.exec(funcString)
			const body = bodyReg.exec(funcString)
			if (body) {
				if (param) {
					const paramArr = param[0].split(',')
					return new Function(...paramArr, body[0])
				} else {
					return new Function(body[0])
				}
			} else {
				return null
			}
		} else {
			return eval(funcString)
		}
	}

	function cloneOtherType(targe, type) {
		const Ctor = targe.constructor
		switch (type) {
			case boolTag:
			case numberTag:
			case stringTag:
			case errorTag:
			case dateTag:
				return new Ctor(targe)
			case regexpTag:
				return cloneReg(targe)
			case symbolTag:
				return cloneSymbol(targe)
			case funcTag:
				return cloneFunction(targe)
			default:
				return null
		}
	}
	// 克隆原始类型
	if (!isObject(target)) {
		return target
	}

	// 初始化
	const type = getType(target)
	let cloneTarget
	if (deepTag.includes(type)) {
		cloneTarget = getInit(target, type)
	} else {
		return cloneOtherType(target, type)
	}

	// 防止循环引用
	if (map.get(target)) {
		return map.get(target)
	}
	map.set(target, cloneTarget)

	// 克隆set
	if (type === setTag) {
		target.forEach(value => {
			cloneTarget.add(clone(value, map))
		})
		return cloneTarget
	}

	// 克隆map
	if (type === mapTag) {
		target.forEach((value, key) => {
			cloneTarget.set(key, deepClone(value, map))
		})
		return cloneTarget
	}

	// 克隆对象和数组
	const keys = type === arrayTag ? undefined : Object.keys(target)
	forEach(keys || target, (value, key) => {
		if (keys) {
			key = value
		}
		cloneTarget[key] = deepClone(target[key], map)
	})

	return cloneTarget
}

/**
 * 计算覆盖物集群的中心点
 * @param {*} features
 * @returns
 */
export function calculateCenter(features) {
	const rectangleBox = {
		minLng: Infinity,
		minLat: Infinity,
		maxLng: -Infinity,
		maxLat: -Infinity
	}
	features.map(item => {
		if (item.WMapTYPE === 'OVERLAYMARKER' || item.WMapTYPE === 'FEATUREMARKER') {
			let flag = item.getPosition()
			if (rectangleBox.minLng > flag[0]) {
				rectangleBox.minLng = flag[0]
			}
			if (rectangleBox.minLat > flag[1]) {
				rectangleBox.minLat = flag[1]
			}
			if (rectangleBox.maxLng < flag[0]) {
				rectangleBox.maxLng = flag[0]
			}
			if (rectangleBox.maxLat < flag[1]) {
				rectangleBox.maxLat = flag[1]
			}
		} else {
			console.warn('暂不支持该类图形或覆盖物')
		}
	})
	return [floatAdd(rectangleBox.minLng, rectangleBox.maxLng) / 2, floatAdd(rectangleBox.minLat, rectangleBox.maxLat) / 2]
}

/**
 * 计算矢量图形中心点相关参数
 * @param {*} geometry
 * @returns
 */
export function calculateGraphCenter(geometry) {
	let center, coordinates, minRadius
	const type = geometry.getType()
	if (type === 'Polygon') {
		let x = 0
		let y = 0
		let i = 0
		coordinates = geometry.getCoordinates()[0].slice(1)
		coordinates.forEach(function (coordinate) {
			x += coordinate[0]
			y += coordinate[1]
			i++
		})
		center = [x / i, y / i]
	} else if (type === 'LineString') {
		center = geometry.getCoordinateAt(0.5)
		coordinates = geometry.getCoordinates()
	} else {
		center = getCenter(geometry.getExtent())
	}
	let sqDistances
	if (coordinates) {
		sqDistances = coordinates.map(function (coordinate) {
			const dx = coordinate[0] - center[0]
			const dy = coordinate[1] - center[1]
			return dx * dx + dy * dy
		})
		minRadius = Math.sqrt(Math.max.apply(Math, sqDistances)) / 3
	} else {
		minRadius = Math.max(getWidth(geometry.getExtent()), getHeight(geometry.getExtent())) / 3
	}
	return {
		center: center,
		coordinates: coordinates,
		minRadius: minRadius,
		sqDistances: sqDistances
	}
}

/**
 * 浮点数相加
 */
export function floatAdd(a, b) {
	let a1 = a.toString().split('.')[1].length
	let b1 = b.toString().split('.')[1].length
	let max = Math.pow(10, Math.max(a1, b1))
	return (a * max + b * max) / max
}

/**
 * 经纬度浮点数精度丢失问题
 * number 原始浮点数
 * precision 保存几位数
 * type: true => 保持Number.toFixed() 返回值类型, false => 将Number.toFixed() 返回值改成数字类型
 */
export function toSureFixed(number, precision, type = false) {
	let numStr = number.toString()
	let maxLen = numStr.length
	let lastStr = numStr.slice(maxLen - 1)
	if (lastStr === '5') {
		lastStr += 1
		numStr = numStr.slice(0, maxLen - 1) + lastStr
		return type ? (numStr - 0).toFixed(precision) : Number((numStr - 0).toFixed(precision))
	} else {
		return type ? number.toFixed(precision) : Number(number.toFixed(precision))
	}
}

/**
 * 矢量图通用样式方法
 * @param {*} option
 * @returns
 */
export function commonStyle(option) {
	if (!option) {
		option = {}
	}
	return new Style({
		geometry: function (feature) {
			const modifyGeometry = feature.get('modifyGeometry')
			return modifyGeometry ? modifyGeometry.geometry : feature.getGeometry()
		},
		// 矢量图形通用默认样式
		// 样式填充
		fill: new Fill({
			// 填充颜色
			color: option.fillColor || 'rgba(37,241,239,0.2)'
		}),
		RegularShape: new RegularShape({}),
		// 笔触
		stroke: new Stroke({
			// 笔触颜色
			color: option.strokeColor || '#264df6',
			// 笔触宽度
			width: option.strokeWidth || 2,
			// 线帽样式 butt、round、 或square
			lineCap: option.lineCap || 'round',
			// 	线连接样式 bevel、round、 或miter
			lineJoin: option.lineJoin || 'round'
		}),
		// 图形样式，主要适用于点样式
		image: new CircleStyle({
			// 半径大小
			radius: option.imageRadius || 3,
			fill: new Fill({
				// 填充颜色
				color: option.imageFill || '#264df6'
			})
		})
	})
}

/**
 * 地图距离转换成 米(圆形)
 * @param {*} Circle
 * @returns
 */
export function formatRadiusToMeters(Circle) {
	let radius
	const center = Circle.getCenter()
	const pointOnPerimeter = [center[0], center[1] + Circle.getRadius()]
	const c1 = transform(center, 'EPSG:3857', 'EPSG:4326')
	const c2 = transform(pointOnPerimeter, 'EPSG:3857', 'EPSG:4326')
	radius = getDistance(c1, c2)
	return radius
}
/**
 * 米 转换成 地图距离
 * @param {*} meters
 * @returns
 */
export function formatMetersToRadius(meters) {
	const metersPerUnit = new Projection({ units: 'm' }).getMetersPerUnit()
	const circleRadius = meters / metersPerUnit
	return circleRadius
}

/**
 * 浏览器dom是否已加载完毕
 * @param {*} fn
 */
export function domReady(fn) {
	let readyState = document.readyState
	if (readyState === 'interactive' || readyState === 'complete') {
		fn()
	} else {
		window.addEventListener('DOMContentLoaded', fn)
	}
}

/**
 * 获取实例化次数
 * @returns
 */
export function getClassCallTimes() {
	function decoratorBefore(fn, beforeFn) {
		return function () {
			let ret = beforeFn.apply(this, arguments)
			if (ret !== false) {
				fn.apply(this, arguments)
			}
		}
	}

	let funTimes = {}

	return function (fun, funName) {
		funName = funName || fun

		if (funTimes[funName]) {
			return funTimes[funName]
		}

		funTimes[funName] = decoratorBefore(fun, function () {
			funTimes[funName].callTimes++
		})

		funTimes[funName].callTimes = 0

		return funTimes[funName]
	}
}

/**
 * 创建 DOM Element
 * @param {*} options
 * @returns
 */
export function createDomElement(options) {
	const { content, angle, id, zIndex, icon, label, offset } = options
	// console.log(options)
	const isLabelHTML = typeof label === 'object'
	// offset  OverlayMarker自身已经处理过偏移
	// container节点处理
	const container = document.createElement('div')
	container.setAttribute('class', `WMap-marker`)
	container.setAttribute('id', `WMap-marker-${id}`)
	container.style.position = 'absolute'
	container.style.zIndex = zIndex

	if (icon) {
		//icon 图标节点处理
		const iconImg = document.createElement('img')
		//icon 图标容器节点处理
		const iconContainer = document.createElement('div')
		iconImg.src = icon
		iconContainer.setAttribute('class', `WMap-marker-icon`)
		iconContainer.style.position = 'absolute'
		if (angle) {
			iconContainer.style['transform-origin'] = `${-offset[0]}px ${-offset[1]}px 0`
			iconContainer.style.transform = `rotate(${angle}deg)`
		}
		iconContainer.appendChild(iconImg)
		icon && !content && container.appendChild(iconContainer)
	}

	if (content) {
		//content 节点处理
		const contentContainer = document.createElement('div')
		contentContainer.setAttribute('class', `WMap-marker-content`)
		contentContainer.style.position = 'absolute'
		contentContainer.innerHTML = content
		if (angle) {
			container.style['transform-origin'] = `${-offset[0]}px ${-offset[1]}px 0`
			container.style.transform = `rotate(${angle}deg)`
		}
		content && container.appendChild(contentContainer)
	}

	if (isLabelHTML) {
		//label 节点处理
		const labelContainer = document.createElement('div')
		labelContainer.setAttribute('class', `WMap-marker-label`)
		labelContainer.style.position = 'absolute'
		if (label.offset) {
			labelContainer.style.left = label.offset[0] - offset[0] + 'px'
			labelContainer.style.top = label.offset[1] - offset[1] + 'px'
		}
		labelContainer.innerHTML = label.content
		isLabelHTML && container.appendChild(labelContainer)
	}

	return container
}

/**
 * 数组差异性比较
 * before => 差异前数组 type: Array<number | string>
 * after => 差异后数组 type: Array<number | string>
 */
export function compareArray(before = [], after = []) {
	let res = {
		add: [],
		remove: []
	}
	let compareMap = new Map()
	for (let i = 0; i < before.length; i++) {
		compareMap.set(JSON.stringify(before[i]), before[i])
	}
	// 差异比较 compareMap中保存的是before有的而after不存在的
	for (let i = 0; i < after.length; i++) {
		if (!compareMap.has(JSON.stringify(after[i]))) {
			res.add.push(after[i])
		} else {
			delete compareMap[after[i]]
		}
	}
	for (let j of compareMap.values()) {
		res.remove.push(j)
	}
	return res
}

// 3857坐标系转4326坐标系
// 经度= 平面坐标x/20037508.34*180
// 纬度= 180/(PI*(2*atan(exp(平面坐标y/20037508.34*180*PI/180))-PI/2)

export function webMercator2lonlat(x, y) {
	let lon = (x / 20037508.34) * 180
	let lat = (y / 20037508.34) * 180
	lat = (180 / Math.PI) * (2 * Math.atan(Math.exp((lat * Math.PI) / 180)) - Math.PI / 2)

	// returen(lonlat)
	return [lon, lat]
}

// 4326坐标系转3857坐标系
// 平面坐标x = 经度*20037508.34/108 平面坐标y = log（tan（（90+纬度）*PI/360））/（PI/360）*20037508.34/180
export function lonlattoWebMercator(lon, lat) {
	let x = (lon * 20037508.34) / 180
	let y = Math.log(Math.tan(((90 + lat) * Math.PI) / 360)) / (Math.PI / 180)
	y = (y * 20037508.34) / 180
	return [x, y]
}

/**
 * 二维数组去重
 * @param {*} arr
 */
export function arrayDup(arr) {
	let res = new Map()
	arr.forEach(item => {
		res.set(JSON.stringify(item), item)
	})
	return Array.from(res.values())
}

/**
 * 对传入的路径集合 进行去重并分段添加虚拟点处理后返回
 * @param {*} path
 * @param {*} segmentLength  // 对传入路径做分段处理，以防有路径间隔过远，默认为 10 m
 * @returns
 */
export function decoratePath(path = [], segmentLength = 1, units = 'meters') {
	if (!Array.isArray(path)) return []
	// 我们先对传进来的路径数组进行去重处理，然后再对整个去重后的linestring进行间隔分段处理
	let dupPath = removeOutlier(arrayDup(path))
	let line = turf.lineString(dupPath)
	// let lineChunk = turf.lineChunk(line, segmentLength, { units: 'meters' })
  let chunk = []; // 结果数组
// 获取线段L的长度
let lineLength = turf.length(line, {units: units});

if (lineLength <= segmentLength) return;

// 将要裁成小线段的数目
let numberOfSegments = lineLength / segmentLength;
if (!Number.isInteger(numberOfSegments)) {
    numberOfSegments = Math.ceil(numberOfSegments);
}

// 循环获取小线段
for (let i = 0; i < numberOfSegments; i++) {
  let outline = turf.lineSliceAlong(line, segmentLength * i, segmentLength * (i + 1), {units: units});
    chunk.push(outline);
}
	let flatArr = arrayDup(
		// lineChunk.features
    chunk
    .filter(item => item.type === 'Feature')
			.map(item => item.geometry.coordinates)
			.flat()
	)
	return flatArr
}

/**
 * 计算两个坐标点之间的角度
 * @param {*} startPos
 * @param {*} endPos
 * @returns
 */
export function computedAngle(startPos, endPos) {
	let point1 = turf.toWgs84(turf.point(startPos))
	let point2 = turf.toWgs84(turf.point(endPos))
	let angle = turf.bearing(point1, point2)
	return angle
}
/**
 * 根据 传入 坐标点获取在路径上的索引
 * @param {Array} coordinates
 * @param {Array} path
 * @param {Number} index
 * @returns index
 */
export function computedPassedIndex(coordinates, path, index) {
	// 驶过路径计算
	let pos1 = path[index]
	let pos2 = path[index + 1]
	let dx1 = pos2[0] - pos1[0]
	let dy1 = pos2[1] - pos1[1]
	// 两个点相同
	if (dx1 == 0 && dy1 == 0) {
		// console.warn(`路径去重失败，失败路径为${pos2}，请检查数组去重方法是否有效执行，函数名为 arrayDup`)
		index++
	}
	let x = toLonLat(coordinates)[0]
	let y = toLonLat(coordinates)[1]
	//检测是否到达pos2点
	let dx2 = x - pos1[0]
	let dy2 = y - pos1[1]
	let tx = dx2 / dx1
	let ty = dy2 / dy1
	let isArrivePos2 = false
	if (isNaN(tx) && ty >= 1) {
		isArrivePos2 = true
	} else if (isNaN(ty) && tx >= 1) {
		isArrivePos2 = true
	} else {
		if (tx >= 1 || ty >= 1) {
			isArrivePos2 = true
		}
	}
	if (isArrivePos2) {
		index++
	}
	return index
}

/**
 * 计算多个点组成的线段总长度
 * @param {*} path 
 */
 export function calcDistance(path) {
  let line = turf.lineString(path)
  let distance = turf.length(line, {units: 'kilometers'})
  return distance
}

/**
 * 判断点是否在线段上
 * @param {*} point 
 * @param {*} path 
 */
export function isPointOnLine(point, path) {
  let pt = turf.point(point)
  let line = turf.lineString(path)
  return turf.booleanPointOnLine(pt, line)
}

/**
 * 计算点到线段的最短距离
 * @param {*} point 
 * @param {*} path 
 */
export function calcShortDistance(point, path) {
  let pt = turf.point(point)
  let line = turf.lineString(path)
  let distance = turf.pointToLineDistance(pt, line, {units: 'kilometers'})
  return distance
}

/**
 * 计算沿该线指定距离的点
 * @param {*} point
 * @param {*} path
 */
 export function calcAlongPoint(path, distance) {
	let line = turf.lineString([path])
	let options = { units: 'kilometers' }

	let along = turf.along(line, distance, options)
	return along
}

/**
 * 获取一个点和一个LineString，并计算(多)LineString上最近的点,以及索引
 * @param {*} path
 * @param {*} point
 */
export function calcNearestPointOnLine(path, point) {
	let line = turf.lineString(path)
	let pt = turf.point(point)
	let nearestPoint = turf.nearestPointOnLine(line, pt, { units: 'kilometers' })
	return { cPoint: nearestPoint.geometry.coordinates, cIndex: nearestPoint.properties.index }
}

 
/**
 * 防抖函数（常用于input框搜索情况）
 * @param {*} func 
 * @param {*} delay 默认500ms
 * @param {*} immediate 
 * @returns 
 */
 export function debounce(func, delay = 500, immediate = true) {
  let timer = null
  return function(args) {
    let _this = this
    if (timer) {
      clearTimeout(timer)
    }
    if (immediate) {
      let now = !timer
      timer = setTimeout(() => {
        timer = null
      }, delay)
      now && func.call(_this, args)
    } else {
      timer = setTimeout(() => {
        timer = null
        func.call(_this, args)
      }, delay)
    }
  }
 }

 
/**
 * 节流函数（常用于onresize, onmouseover情况）
 * @param {*} func 
 * @param {*} delay 默认500ms
 * @param {*} immediate 
 * @returns 
 */
 export function throttle(func, delay = 500, immediate = true) {
  let timer = null
  return function (args) {
    let _this = this
    if (!timer) {
      if (immediate) {
        func.call(_this, args)
        timer = setTimeout(() => {
          timer = null
        }, delay)
      } else {
        timer = setTimeout(() => {
          func.call(_this, args)
          timer = null
        }, delay)
      }
    }
  }
}

/**
 * @description 去除array中前后项对比差值超过difference
 * @export	removeOutlier
 * @param { array } array
 * @param { number } difference 1
 * @return {array}
 */
 export function removeOutlier(array = [], difference = 1) {
	if (!Array.isArray(array) || array.length < 1) return []
	const differenceMore = (array, i) =>
		(Math.abs(array[i + 1][0] - array[i][0]) >= difference && Math.abs(array[i + 1][0] - array[i + 2][0]) >= difference) ||
		(Math.abs(array[i + 1][1] - array[i][1]) >= difference && Math.abs(array[i + 1][1] - array[i + 2][1]) >= difference)
	try {
		const newArray = array.reduce((pre, item, i, array) => {
			if (array.length < 3) return [...pre, item]
			return i < array.length - 2 && differenceMore(array, i) ? pre : array[i + 1] ? [...pre, array[i + 1]] : pre
		}, [])
		return newArray
	} catch (error) {
		return array
	}
}
